import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import sun.misc.BASE64Encoder;

/**
 *
 * @author Alexandre
 * @author Lucas
 */
public class Servidor {

    static int PORTA = 5000;

    /**
     * @param args the command line arguments
     * @throws java.net.SocketException
     * @throws java.security.NoSuchAlgorithmException
     * @throws javax.crypto.NoSuchPaddingException
     * @throws java.security.InvalidKeyException
     * @throws javax.crypto.IllegalBlockSizeException
     * @throws javax.crypto.BadPaddingException
     * @throws java.security.SignatureException
     *
     */
    public static void main(String[] args) throws SocketException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, SignatureException {
        System.out.println("Servidor executando");
        
        DatagramSocket serverSocket = new DatagramSocket(PORTA);
        DatagramPacket pacoteEnvio, pacoteRecebido;


        //Passo 1: obter instância
        Cipher cifra = Cipher.getInstance("RSA");
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");

        //Geração das chaves
        keyGen.initialize(512);
        KeyPair parChaves = keyGen.generateKeyPair();
        PublicKey chavePublica = parChaves.getPublic();
        PrivateKey chavePrivada = parChaves.getPrivate();

        /**
         * Gerando a assinatura
         */
        Signature geradorAssin = Signature.getInstance("SHA1withRSA");
        geradorAssin.initSign(chavePrivada);

        //mensagem da assinatura
        String textoAssin = "Sign IN";
        geradorAssin.update(textoAssin.getBytes());

        byte[] assinatura = geradorAssin.sign();

        while (true) {
            byte[] receiveData = new byte[64];
            pacoteRecebido
                    = new DatagramPacket(
                            receiveData, receiveData.length);

            serverSocket.receive(pacoteRecebido);

            InetAddress IPCliente
                    = pacoteRecebido.getAddress();
            int port = pacoteRecebido.getPort();

            byte[] dadosPacoteRecebido = pacoteRecebido.getData();

            String auxMsg = new String(receiveData);
            auxMsg = auxMsg.trim();
            
            if ("iniciar".equals(auxMsg)) {

                pacoteEnvio
                        = new DatagramPacket(chavePublica.getEncoded(),
                                chavePublica.getEncoded().length,
                                IPCliente, port);

                serverSocket.send(pacoteEnvio);
                
                System.out.println("Chave pública enviada para o cliente");
                
            } else {
                if ("exit".equals(auxMsg)) {
                    System.exit(1);
                } else {
                    if ("assinatura".equals(auxMsg)) {
                        /**
                         * Enviando o texto claro
                         */
                        pacoteEnvio
                                = new DatagramPacket(textoAssin.getBytes(),
                                        textoAssin.getBytes().length,
                                        IPCliente, port);

                        serverSocket.send(pacoteEnvio);
                        System.out.println("Mensagem da assinatura enviada para o cliente");
                        /**
                         * Envia a assinatura
                         */
                        pacoteEnvio
                                = new DatagramPacket(assinatura,
                                        assinatura.length,
                                        IPCliente, port);

                        serverSocket.send(pacoteEnvio);
                        System.out.println("Assinatura enviada para o cliente");
                    } else {
                        System.out.println("Texto Cifrado: " + encodeBase64(dadosPacoteRecebido));
                        //Decifrar a mensagem
                        cifra.init(Cipher.DECRYPT_MODE, chavePrivada);
                        byte[] TEXTO_DECIFRADO = cifra.doFinal(dadosPacoteRecebido);
                        System.out.println("Texto Decifrado: " + new String(TEXTO_DECIFRADO));
                    }
                }
            }
        }
    }

    private static String encodeBase64(byte[] encoded) {
        BASE64Encoder b64 = new BASE64Encoder();
        return b64.encode(encoded);
    }
}
